const chokidar = require('chokidar');
const Waiter = require('./util/waiter');
const PackageManager = require('./pkger');
const Path = require('path');

const AdaPack = {
    map: {},
    get(tag) {
        if (!this.map[tag]) {
            this.map[tag] = new PackageManager({ tag });
        }
        let manager = this.map[tag];
        return manager.ready().then(() => {
            return manager.config;
        });
    },
    develop(fn) {
        let manager = this.map['develop'] || new PackageManager({ tag: 'develop' });
        let pathSet = new Set();
        return manager.ready().then(() => {
            let { packers, pager } = manager;
            return Promise.resolve().then(() => pager.outputAda()).then(() => {
                return packers.reduce((a, packer) => {
                    return a.then(() => packer.pack());
                }, Promise.resolve());
            }).then(() => pager.outputIndex()).then(() => {
                return packers.forEach((packer) => {
                    packer.getWatchPaths().forEach(a => pathSet.add(a));
                });
            }).then(() => {
                return { packers, pager };
            });
        }).then(({ packers, pager }) => {
            let waiter = new Waiter();
            chokidar.watch([...pathSet], {
                ignored: [
                    /(^|[\/\\])\../,
                    ...[...pathSet].
                        filter(a => a.indexOf('node_modules') !== -1).
                        map(a => (Path.resolve(a, './node_modules/') + '/').replace(/\\/g, ''))
                ],
                ignoreInitial: true,
            })
                .on('change', path => waiter.add("edit", path))
                .on('add', path => waiter.add("add", path))
                .on('unlink', path => waiter.add("remove", path))
                .on("ready", () => {
                    waiter.setHandler(({ add, edit, remove }) => {
                        let out = [];
                        return packers.reduce((a, packer) => {
                            return a.then(() => {
                                return Promise.resolve().then(() => {
                                    return packer.repackByFilesChange('add', add).then(info => out.push(info));
                                }).then(() => {
                                    return packer.repackByFilesChange('edit', edit).then(info => out.push(info));
                                }).then(() => {
                                    return packer.repackByFilesChange('remove', remove).then(info => out.push(info));
                                });
                            });
                        }, Promise.resolve()).then(() => {
                            return pager.outputIndex();
                        }).then(()=>{
                            return manager.cleanCache();
                        }).then(() => {
                            out.filter(a => !!a).forEach(a => {
                                fn && fn(a);
                            });
                        });
                    });
                });
            return packers;
        });
    },
    publish() {
        let manager = this.map['publish'] || new PackageManager({ tag: 'publish' });
        return manager.ready().then(() => {
            let { packers, pager } = manager;
            return Promise.resolve().then(() => pager.outputAda()).then(() => {
                return packers.reduce((a, next) => {
                    return a.then(() => next.pack());
                }, Promise.resolve());
            }).then(() => pager.outputIndex()).then(()=>{
                return manager.cleanCache();
            });
        });
    }
};

module.exports = AdaPack;